﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using IOLib;

namespace SheetLib
{
    public class BlockControl : BaseControl
    {
        [Flags]
        private enum ArrowTypes
        {
            None = 0,
            RightFrom =1,
            DownFrom=2,
            LeftFrom=4,
            UpFrom = 8,
            RightTo =16,
            DownTo = 32,
            LeftTo = 64,
            UpTo = 128
        }
        private const int HorArrowWidth = 5;
        private const int HorArrowHeight = 7;
        private const int VertArrowWidth = 5;
        private const int VertArrowHeight = 4;

        private ArrowTypes drawnArrows;
        private ArrowTypes realArrows;
        public Bitmap blockBmp;
        private int index;
        private string type;
        public int X;
        public int Y;

        private Matan.Block mathModel = null;


        public BlockControl(string type, int index)
        {
            this.index = index;
            this.type = type;
            // Vanya code =))))
            this.mathModel = Matan.Block.GetParticularBlock(type);
            if (this.mathModel == null)
            {
                throw new Exception("Unsupported block type!");
            }
            this.mathModel.Number = index;
            // End Vanya code
            switch (type)
            {
                case "G": this.realArrows = ArrowTypes.RightFrom;
                          this.blockBmp = BlockBitmapCreator.G(index);
                    break;
                case "K": this.realArrows = ArrowTypes.LeftTo | ArrowTypes.RightFrom;
                          this.blockBmp = BlockBitmapCreator.K(index);
                    break;
                case "W": this.realArrows = ArrowTypes.LeftTo | ArrowTypes.RightFrom;
                          this.blockBmp = BlockBitmapCreator.W(index);
                    break;
                case "W2": this.realArrows = ArrowTypes.LeftTo | ArrowTypes.RightFrom;
                           this.blockBmp = BlockBitmapCreator.W2(index);
                    break;
                case "F": this.realArrows = ArrowTypes.LeftTo | ArrowTypes.RightFrom;
                          this.blockBmp = BlockBitmapCreator.F(index);
                    break;
                case "F2": this.realArrows = ArrowTypes.UpTo | ArrowTypes.DownTo | ArrowTypes.RightFrom;
                           this.blockBmp = BlockBitmapCreator.F2(index);
                    break;
                case "+": this.realArrows = ArrowTypes.UpTo | ArrowTypes.LeftTo | ArrowTypes.DownTo | ArrowTypes.RightFrom;                    
                    this.blockBmp = BlockBitmapCreator.Summator(
                        this.MathModel.Parameters                            
                            .OrderBy(p => p.Key)
                            .Select(p => (bool)p.Value).ToArray()
                    );
                    break;
                case "Y": this.realArrows = ArrowTypes.LeftTo;
                          this.blockBmp = BlockBitmapCreator.Y(index);
                    break;
                default:
                    throw new Exception("Unsupported block type!");
            }
            Size size = blockBmp.Size;
            size.Height+=9;
            size.Width+=9;
            this.Size = size;
            this.InDate = true;
            this.drawnArrows = this.realArrows;
        }
        public override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            Graphics g = e.Graphics;
            if (this.Focused) g.FillRectangle(base.brush[1], e.ClipRectangle);
            g.DrawImage(blockBmp, e.ClipRectangle.X, e.ClipRectangle.Y);
            DrawArrows(e);
        }
        public override void OnMouseDown(MouseEventArgs e)
        {
            if (!this.Focused)
            {
                this.Focused = true;
                this.PrePainter();
                this.RePainter();
            }
        }

        private void DrawArrows(PaintEventArgs e)
        {
            using (Pen pen = new Pen(Color.Black))
            {
                if ((drawnArrows & ArrowTypes.RightFrom) != ArrowTypes.None)
                {
                    int x = e.ClipRectangle.X + blockBmp.Width;
                    int y = e.ClipRectangle.Y + blockBmp.Height / 2;
                    e.Graphics.DrawLines(pen, new Point[] { new Point(x - HorArrowWidth,y - HorArrowHeight / 2),
                                                            new Point(x, y),
                                                            new Point(x - HorArrowWidth, y + HorArrowHeight / 2)});
                }
                if ((drawnArrows & ArrowTypes.LeftFrom) != ArrowTypes.None)
                {
                    int x = e.ClipRectangle.X;
                    int y = e.ClipRectangle.Y + blockBmp.Height / 2;
                    e.Graphics.DrawLines(pen, new Point[] { new Point(x + HorArrowWidth, y - HorArrowHeight / 2),
                                                            new Point(x, y),
                                                            new Point(x + HorArrowWidth, y + HorArrowHeight / 2)});
                }
                if ((drawnArrows & ArrowTypes.RightTo) != ArrowTypes.None)
                {
                    int x = e.ClipRectangle.X + blockBmp.Width;
                    int y = e.ClipRectangle.Y + blockBmp.Height / 2;
                    e.Graphics.DrawLines(pen, new Point[] { new Point(x, y - HorArrowHeight / 2),
                                                            new Point(x - HorArrowWidth, y),
                                                            new Point(x, y + HorArrowHeight / 2)});
                }
                if ((drawnArrows & ArrowTypes.DownTo) != ArrowTypes.None)
                {
                    int x = e.ClipRectangle.X + blockBmp.Width/2;
                    int y = e.ClipRectangle.Y + blockBmp.Height ;
                    e.Graphics.DrawLines(pen, new Point[] { new Point(x - VertArrowWidth / 2, y),
                                                            new Point(x, y - VertArrowHeight-1),
                                                            new Point(x + VertArrowWidth / 2, y)});
                }
                if ((drawnArrows & ArrowTypes.LeftTo) != ArrowTypes.None)
                {
                    int x = e.ClipRectangle.X;
                    int y = e.ClipRectangle.Y + blockBmp.Height / 2;
                    e.Graphics.DrawLines(pen, new Point[] { new Point(x, y - HorArrowHeight / 2),
                                                            new Point(x + HorArrowWidth, y),
                                                            new Point(x, y + HorArrowHeight / 2)});
                }
                if ((drawnArrows & ArrowTypes.UpTo) != ArrowTypes.None)
                {
                    int x = e.ClipRectangle.X + blockBmp.Width / 2;
                    int y = e.ClipRectangle.Y;
                    e.Graphics.DrawLines(pen, new Point[] { new Point(x - VertArrowWidth / 2, y),
                                                            new Point(x, y + VertArrowHeight),
                                                            new Point(x + VertArrowWidth / 2, y)});
                }
            }
        }

        public override void AddAssociatedConnector(ConnectionPlace place, Connector c)
        {
            base.AddAssociatedConnector(place, c);
            c.Disposed += new Connector.ConnectorDesposedEventHandler(Connector_Disposed);
            switch (place)
            {
                case ConnectionPlace.Right: 
                    {
                        if (((realArrows & ArrowTypes.RightFrom) | (realArrows & ArrowTypes.RightTo)) ==
                            ArrowTypes.None)
                        {
                            throw new NotSupportedException();
                        }
                        else
                        {
                            drawnArrows &= ~ArrowTypes.RightFrom;
                            drawnArrows &= ~ArrowTypes.RightTo;
                        }
                        break;
                    }
                case ConnectionPlace.Down:
                    {
                        if (((realArrows & ArrowTypes.DownFrom) | (realArrows & ArrowTypes.DownTo)) ==
                            ArrowTypes.None)
                        {
                            throw new NotSupportedException();
                        }
                        else
                        {
                            drawnArrows &= ~ArrowTypes.DownFrom;
                            drawnArrows &= ~ArrowTypes.DownTo;
                        }
                        break;
                    }
                case ConnectionPlace.Left:
                    {
                        if (((realArrows & ArrowTypes.LeftFrom) | (realArrows & ArrowTypes.LeftTo)) ==
                            ArrowTypes.None)
                        {
                            throw new NotSupportedException();
                        }
                        else
                        {
                            drawnArrows &= ~ArrowTypes.LeftFrom;
                            drawnArrows &= ~ArrowTypes.LeftTo;
                        }
                        break;
                    }
                case ConnectionPlace.Up:
                    {
                        if (((realArrows & ArrowTypes.UpFrom) | (realArrows & ArrowTypes.UpTo)) ==
                            ArrowTypes.None)
                        {
                            throw new NotSupportedException();
                        }
                        else
                        {
                            drawnArrows &= ~ArrowTypes.UpFrom;
                            drawnArrows &= ~ArrowTypes.UpTo;
                        }
                        break;
                    }
            }
        }

        void Connector_Disposed(Connector sender)
        {
            for (int i = 0; i < this.associatedConnectors.Length; i++)
            {
                for (int j = 0; j < associatedConnectors[i].Count; j++)
                {
                    if (associatedConnectors[i][j] == sender)
                    {
                        if (associatedConnectors[i].Count == 1)
                        {
                            switch (i)
                            {
                                case 0: this.drawnArrows |= (ArrowTypes.RightFrom | ArrowTypes.RightTo) &
                                    realArrows; break;
                                case 1: this.drawnArrows |= (ArrowTypes.DownFrom | ArrowTypes.DownTo) &
                                    realArrows; break;
                                case 2: this.drawnArrows |= (ArrowTypes.LeftFrom | ArrowTypes.LeftTo) &
                                    realArrows; break;
                                case 3: this.drawnArrows |= (ArrowTypes.UpFrom | ArrowTypes.UpTo) &
                                    realArrows; break;
                            }
                        }
                        associatedConnectors[i].RemoveAt(j);
                        return;
                    }
                }
            }
        }
        public override Point GetConnectionPoint(ConnectionPlace connectionPlace)
        {
            switch (connectionPlace)
            {
                case ConnectionPlace.Right: return new Point(this.Location.X + this.Size.Width-5,
                    this.Location.Y + this.Size.Height / 2);
                case ConnectionPlace.Down: return new Point(this.Location.X + this.Size.Width / 2,
                    this.Location.Y + this.Size.Height-5);
                case ConnectionPlace.Left: return new Point(this.Location.X + 4,
                    this.Location.Y + this.Size.Height / 2);
                case ConnectionPlace.Up: return new Point(this.Location.X + this.Size.Width / 2,
                    this.Location.Y+4);
                default: throw new ArgumentException();
            }
        }
        public override HotSpot[] GetConnHotSpots(bool start)
        {
            int radius = 4;
            List<HotSpot> hss = new List<HotSpot>();
            if (start)
            {
                if ((this.realArrows & ArrowTypes.RightFrom) != ArrowTypes.None)
                {
                    hss.Add(new HotSpot(this, new GraphicsPath(), ConnectionPlace.Right));
                    hss[hss.Count - 1].Area.AddEllipse(this.Right - 5 - radius,
                        this.Location.Y + this.Size.Height / 2 - radius, 2 * radius, 2 * radius);
                }
                if ((this.realArrows & ArrowTypes.DownFrom) != ArrowTypes.None)
                {
                    hss.Add(new HotSpot(this, new GraphicsPath(), ConnectionPlace.Down));
                    hss[hss.Count - 1].Area.AddEllipse(this.Location.X + this.Size.Width / 2 - radius,
                        this.Bottom - 5 - radius, 2 * radius, 2 * radius);
                }
                if ((this.realArrows & ArrowTypes.LeftFrom) != ArrowTypes.None)
                {
                    hss.Add(new HotSpot(this, new GraphicsPath(), ConnectionPlace.Left));
                    hss[hss.Count - 1].Area.AddEllipse(this.Location.X + 4 - radius,
                        this.Location.Y + this.Size.Height / 2 - radius, 2 * radius, 2 * radius);
                }
                if ((this.realArrows & ArrowTypes.UpFrom) != ArrowTypes.None)
                {
                    hss.Add(new HotSpot(this, new GraphicsPath(), ConnectionPlace.Up));
                    hss[hss.Count - 1].Area.AddEllipse(this.Location.X + this.Size.Width / 2 - radius,
                        this.Location.Y + 4 - radius, 2 * radius, 2 * radius);
                }
            }
            else
            {
                if ((this.drawnArrows & ArrowTypes.RightTo) != ArrowTypes.None)
                {
                    hss.Add(new HotSpot(this, new GraphicsPath(), ConnectionPlace.Right));
                    hss[hss.Count - 1].Area.AddEllipse(this.Right - 5 - radius,
                        this.Location.Y + this.Size.Height / 2 - radius, 2 * radius, 2 * radius);
                }
                if ((this.drawnArrows & ArrowTypes.DownTo) != ArrowTypes.None)
                {
                    hss.Add(new HotSpot(this, new GraphicsPath(), ConnectionPlace.Down));
                    hss[hss.Count - 1].Area.AddEllipse(this.Location.X + this.Size.Width / 2 - radius,
                        this.Bottom - 5 - radius, 2 * radius, 2 * radius);
                }
                if ((this.drawnArrows & ArrowTypes.LeftTo) != ArrowTypes.None)
                {
                    hss.Add(new HotSpot(this, new GraphicsPath(), ConnectionPlace.Left));
                    hss[hss.Count - 1].Area.AddEllipse(this.Location.X + 4 - radius,
                        this.Location.Y + this.Size.Height / 2 - radius, 2 * radius, 2 * radius);
                }
                if ((this.drawnArrows & ArrowTypes.UpTo) != ArrowTypes.None)
                {
                    hss.Add(new HotSpot(this, new GraphicsPath(), ConnectionPlace.Up));
                    hss[hss.Count - 1].Area.AddEllipse(this.Location.X + this.Size.Width / 2 - radius,
                        this.Location.Y + 4 - radius, 2 * radius, 2 * radius);
                }
            }
            return hss.ToArray();
        }

        public int Index
        {
            get { return index; }
        }
        public string Type
        {
            get { return type; }
        }
        public Matan.Block MathModel
        {
            get { return mathModel; }
        }
    }
}
